classdef AbstractServerModule < handle
    %ABSTRACTSERVERMODULE �˴���ʾ�йش����ժ�?
    %   �˴���ʾ��ϸ˵��
    
    properties
        server
        devices
        module_name
        module_root
        module_handle

        settings        
        subscribed_path
        measured_data
        monitor_hdl
    end

    properties (Access={?EquipmentControl.ZI.ziServer.ServerModules.DataAcquisitionModule, ...
                        ?EquipmentControl.ZI.ziServer.ServerModules.SweeperModule, ...
                        ?EquipmentControl.ZI.ziServer.ServerModules.ScopeModule})
        settingFields
    end
    
    methods
        function obj = AbstractServerModule(server, name, root)
            %ABSTRACTSERVERMODULE ��������ʵ��
            %   �˴���ʾ��ϸ˵��
            p=inputParser;
            p.addRequired('server', @(x) isa(x, 'EquipmentControl.ZI.ziServer.ziServer'));
            p.addRequired('name', @ischar);
            p.addRequired('root', @(x) ismember(x, {'dataAcquisitionModule', 'multiDeviceSyncModule', 'scopeModule', 'sweep'}));
            p.parse(server, name, root);
            
            obj.server = server;
            obj.module_name = name;
            obj.module_root = root;
            obj.devices = obj.server.deviceSerials;
        end
        
        function obj = initialize(obj)
            L = log4m.getLogger;
            if ~isempty(obj.module_handle)
                L.info('AbstractServerModule::initialize', ...
                   sprintf('Old module handle [%d] of [%s] Module [%s] cleared.',obj.module_handle, obj.module_root, obj.module_name));
                ziDAQ('clear', obj.module_handle);
            end
            
            obj.module_handle = ziDAQ(obj.module_root);
            L.info('AbstractServerModule::AbstractServerModule', sprintf('Module Handle [%d] created.',obj.module_handle));

            obj.subscribed_path = {};

            obj.updateModuleSettings();
            obj.createMonitor();
        end
        
        function delete(obj)
            L = log4m.getLogger;
            L.info('AbstractServerModule::delete', ...
                   sprintf('Module handle [%d] of [%s] Module [%s] destructed.',obj.module_handle, obj.module_root, obj.module_name));
            ziDAQ('clear', obj.module_handle);
            
            try
                close(obj.monitor_hdl); 
            catch
                % no monitor window
            end
        end
        
        function help(obj, node)
            obj.updateModuleSettings();
            
            if nargin == 1
                matchNode = obj.settingFields;
            else
                isInFields = contains(obj.settingFields, node);
                settingDescription = cellfun(@(x) obj.settings.(x).Description, obj.settingFields, 'UniformOutput', false);
                isInDescription = contains(settingDescription, node);
                matchNode = obj.settingFields(isInFields|isInDescription);
            end
            
            if ~isempty(matchNode)
                cellfun(@(x) disp(obj.settings.(x)), matchNode);
            else
                fprintf('\n\tNo matched node found ["%s"].\n', node);
            end
        end
        
        %%
        function obj = subscribe(obj, signal_path)
            L=log4m.getLogger;
            ziDAQ('subscribe', obj.module_handle, signal_path);
            L.info('AbstractServerModule::subscribe', sprintf('Path [%s] subscribed for [%s].', signal_path, obj.module_name));
        end
        function obj = unsubscribe(obj, signal_path)
            L=log4m.getLogger;
            if nargin == 1
                signal_path = '*';
                obj.subscribed_path = {};
            else
                if ~ismember(signal_path, obj.subscribed_path)
                    error('Path [%s] has not been subscribed.', signal_path);
                else
                    [~, idx] = ismember(signal_path, obj.subscribed_path);
                    obj.subscribed_path(idx) = [];
                end
            end
            ziDAQ('unsubscribe', obj.module_handle, signal_path);
            L.info('AbstractServerModule::unsubscribe', sprintf('Path [%s] subscribed for [%s].', signal_path, obj.module_name));
        end
        
        %%
        function obj = sync(obj, tPause)
            L=log4m.getLogger;
            ziDAQ('sync'); 
            if nargin > 1 && tPause > 0
                pause(tPause);
            end
            L.info('AbstractServerModule::sync', 'System Synchronized.');
        end
        function obj = execute(obj)
            L=log4m.getLogger;
            ziDAQ('execute', obj.module_handle);
            L.info('AbstractServerModule::execute', sprintf('Executing module [%s]...', obj.module_name));
        end
        
        function obj = finish(obj)
            L=log4m.getLogger;
            ziDAQ('finish', obj.module_handle);
            L.info('AbstractServerModule::finish', sprintf('Finish module [%s].', obj.module_name));
        end
        
        function tf = finished(obj)
            L=log4m.getLogger;
            tf = ziDAQ('finished', obj.module_handle);
            L.trace('AbstractServerModule::finished', sprintf('Module [%s] finished? [%d]', obj.module_name, tf));
        end
        
        function prog = progress(obj)
            L=log4m.getLogger;
            prog = ziDAQ('progress', obj.module_handle);
            L.trace('AbstractServerModule::progress', sprintf('Module [%s] progress = [%.2f%%]', obj.module_name, prog*100));
        end
        
        function data = read(obj)
            L=log4m.getLogger;
            data = ziDAQ('read', obj.module_handle);
            L.trace('AbstractServerModule::read', sprintf('Data read from Module [%s]', obj.module_name));
        end
        
        function obj = createMonitor(obj)
            % create a monitor window
            obj.monitor_hdl = figure('name',['[Monitor]: ' obj.module_name]);  % create a new figure before getting an axes.
        end
        
        function rate = enableDataTransfer(obj, device, node)
            rate = obj.server.enableDataTransfer(device, node);
        end
        
        function [new, old] = setDataTransferRate(obj, device, node, rate)
            [new, old] = obj.server.setDataTransferRate(device, node, rate);
        end
        
        %%
%         function printfnode(obj)            
%             for k = 1:length(obj.settingFields)
%                 field = obj.settingFields{k};
%                 fieldc = strsplit(field, '_');
%                 field1 = strjoin(fieldc(2:end), '_');
%                 field2 = strjoin({field1, repmat(' ', 1, 25-length(field1))});
%                 node = obj.getNode(field);
%                 space50 = repmat(' ', 1, 50-length(node));
%                 
%                 isReadOnly = ~contains(obj.getProperties(field), 'Write');                
%                 type = obj.getType(field);
%                 
%                 opt = obj.getOptionStr(field);
%                 switch type
%                     case 'Double'
%                         typestr = 'double';
%                     case {'Integer (64 bit)', 'Integer (enumerated)'}
%                         typestr = 'int64';
%                     case 'String'
%                         typestr = 'string';                        
%                 end
%                 space8 = repmat(' ', 1, 8-length(typestr));
%                 fprintf('%s (''%s'',%s%d, ''%s'',%s%s)\n', field2, node,space50, isReadOnly, typestr, space8, opt);
%             end
%         end

    end
    
    %%
    methods
        function obj = updateModuleSettings(obj)
            obj.settings = jsondecode(ziDAQ('listNodesJSON', obj.module_handle, obj.module_root, 3));
            obj.settingFields = fields(obj.settings);
            switch obj.module_root
                case 'dataAcquisitionModule'
                    for k = 1:length(obj.settingFields)
                        [~, node] = obj.nodeSplit(obj.settingFields{k});
                        obj.settings.(obj.settingFields{k}).Value = obj.getModuleSettingValue(ziSettingsDAQModule.(node));
                    end
                case 'scopeModule'
                    for k = 1:length(obj.settingFields)
                        [~, node] = obj.nodeSplit(obj.settingFields{k});
                        obj.settings.(obj.settingFields{k}).Value = obj.getModuleSettingValue(ziSettingsScopeModule.(node));
                    end
                case 'sweep'
                    for k = 1:length(obj.settingFields)
                        [~, node] = obj.nodeSplit(obj.settingFields{k});
                        obj.settings.(obj.settingFields{k}).Value = obj.getModuleSettingValue(ziSettingsSweeperModule.(node));
                    end
                case 'multiDeviceSyncModule'
                    for k = 1:length(obj.settingFields)
                        [~, node] = obj.nodeSplit(obj.settingFields{k});
                        obj.settings.(obj.settingFields{k}).Value = obj.getModuleSettingValue(ziSettingsMultiDeviceSyncModule.(node));
                    end
            end
        end
        
        function val = getModuleSettingValue(obj, node)
            p = inputParser;
            p.addRequired('node', @(x) isa(x, 'ziSettings'));
            p.parse(node)            
            switch node.dataType
                case 'double'
                    val = ziDAQ('getDouble', obj.module_handle, node.toString);
                case 'int64'
                    val = ziDAQ('getInt', obj.module_handle, node.toString);
                case 'string'
                    val = ziDAQ('getString', obj.module_handle, node.toString);
            end            
        end
        
        function obj = setModuleSettingValue(obj, node, val)
            p = inputParser;
            p.addRequired('node', @(x) isa(x, 'ziSettings'));
            p.parse(node)
            
            nodeString = node.toString;
            switch node.dataType
                case 'double'
                    ziDAQ('set', obj.module_handle, nodeString, double(val) );
                case 'int64'
                    ziDAQ('set', obj.module_handle, nodeString, int64(val) );
                case 'string'
                    ziDAQ('set', obj.module_handle, nodeString, num2str(val) );
            end            
        end
        
        function [root, node] = nodeSplit(~, node, delimiter)
            if nargin == 2
                delimiter = '_';
            end
            strCell = strsplit(node, delimiter);
            root=strCell{1};
            
            if length(strCell) > 2
                node = strjoin(strCell(2:end), '_');
            else
                node = strCell{2};
            end                
        end
    end
end
